import { Alert, ConfigProvider, Modal } from 'antd'
import zhCN from 'antd/locale/zh_CN'
import { uniqueId } from 'lodash-es'
import React, { useEffect, useRef, useState } from 'react'
import ReactDom from 'react-dom/client'

import { theme } from '@/utils/antdHelper'

import AekForm from '../AekForm'

const AekModalContext = React.createContext()

/**
 * 打开弹框
 * 不可传入onSubmit和onCancel，因为PromiseThen会传入
 */
export function PromiseThen(props = {}) {
  const Component = this
  if (!Component.aekUuid) {
    Component.aekUuid = `promiseThen-uuid-${uniqueId()}`
  }
  const key = `modalRoot-${Component.aekUuid}`
  return new Promise((resolve, reject) => {
    const wrapper = document.getElementById('modalRoot')
    let child = document.getElementById(key)
    if (!child) {
      child = document.createElement('div')
      child.id = key
      wrapper.appendChild(child)
    }
    const root = ReactDom.createRoot(child)

    const onCancel = () => {
      root.unmount()
      reject(Error('关闭Modal'))
    }
    const onSubmit = res => {
      root.unmount()
      resolve(res)
    }

    root.render(
      <ConfigProvider locale={zhCN} theme={theme}>
        <AekModalContext.Provider value={{ onCancel, onSubmit }}>
          <Component {...props} onSubmit={onSubmit} onCancel={onCancel} />
        </AekModalContext.Provider>
      </ConfigProvider>,
    )
  })
}

/**
 * @typedef {Object} OtherProps
 * @property {() => Promise} [onOk] 弹框点确认回调
 * @property {boolean} [showFooter] 是否显示底部按钮
 * @property {({onOk: Promise, onClose: Function}) => React.ReactNode} [renderFooter] 构建footer
 */

/**
 * @typedef {import("antd/es/modal").ModalProps & OtherProps} AekModalProps
 */

/**
 * 打开弹框
 * @param {AekModalProps} param0
 * @returns
 */
const AekModal = ({
  children,
  onOk: wrapperOnOk = async _ => _,
  showFooter = true,
  renderFooter = () => undefined,
  afterClose = _ => _,
  ...otherProps
}) => {
  const ref = useRef({})
  const [confirmLoading, setConfirmLoading] = useState(false)
  const [open, setVisible] = useState(false)

  useEffect(() => {
    setVisible(true)
  }, [])

  const onClose = () => {
    ref.current.clickOnClose = true
    setVisible(false)
  }

  const onOk = () => {
    setConfirmLoading(true)
    wrapperOnOk()
      .then(reason => {
        ref.current.reason = reason
        ref.current.clickOnOk = true
        setVisible(false)
      })
      .finally(() => {
        setConfirmLoading(false)
      })
  }

  return (
    <AekModalContext.Consumer>
      {props => {
        const { onCancel, onSubmit } = props
        return (
          <Modal
            {...{
              open: open,
              maskClosable: false,
              confirmLoading,
              ...otherProps,
              onCancel: onClose,
              footer: originNode =>
                showFooter ? renderFooter({ onOk, onClose }) || otherProps.footer || originNode : null,
              onOk,
              afterClose: () => {
                // Modal内部在afterClose还去改变了内部的state，所以让下面的延后执行，避免报错
                setTimeout(() => {
                  if (ref.current.clickOnClose) {
                    onCancel()
                    afterClose()
                  }
                  ref.current.clickOnOk && onSubmit(ref.current.reason)
                }, 0)
              },
            }}
          >
            <Alert.ErrorBoundary>{children}</Alert.ErrorBoundary>
          </Modal>
        )
      }}
    </AekModalContext.Consumer>
  )
}

/**@type {(props?: AekModalProps) => Promise<any>} */
AekModal.PromiseThen = PromiseThen

/**
 * @typedef {Object} PromiseFormProps
 * @property {import("@/components/AekForm").AekFormProps['formData']} [formData] 表单配置
 */

/**
 * 辅助处理需要表单验证的弹窗
 * @param {import("antd/es/modal").ModalProps & PromiseFormProps} param0
 * @returns
 */
export const PromiseForm = ({ title = '标题', formData, ...otherProps } = {}) => {
  const [form] = AekForm.useForm()

  const onOk = async () => {
    const values = await form.validateFields()
    return values
  }

  return (
    <AekModal title={title} onOk={onOk} {...otherProps}>
      <AekForm form={form} formData={formData} />
    </AekModal>
  )
}

/**@type {(props?: import("antd/es/modal").ModalProps & PromiseFormProps) => Promise<any>} */
PromiseForm.PromiseThen = PromiseThen

export default AekModal
